<?php
namespace App\Core\App;
use App\Core\Container;
use App\Core\Logs;
use App\Core\Responses\Response as MyResponse;
use Swoole\Exception;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;

class SwooleHttpApp extends BaseApp
{
    /**
     * master进程名
     * @var string
     */
    public string $masterProcessName = 'php-fast-master';

    /**
     * manager进程名
     * @var string
     */
    public string $managerProcessName = 'php-fast-manager';

    /**
     * work进程名
     * @var string
     */
    public string $workerProcessName = 'php-fast-worker';

    /**
     * 控制器缓存数组
     * @var array
     */
    public array $controllers = [];

    /**
     * 默认控制器
     * @var string
     */
    public string $defaultController = 'index';

    /**
     * 默认action
     * @var string
     */
    public string $defaultAction = 'test';

    /**
     * 默认server配置
     * @var array
     */
    public array $defaultConfig = [
        'host'      => '127.0.0.1',
        'port'      => 99,
        'is_base'   => true,
        'set'       => [
            'reactor_num'       => APP_CPU_NUM,
            'worker_num'        => APP_CPU_NUM,
            'daemonize'         => false,
            'enable_reuse_port' => true,
            'log_file'          => __DIR__.'/../../../runtime/server/server.log',
            'log_level'         => SWOOLE_LOG_NOTICE,
            'log_rotation'      => SWOOLE_LOG_ROTATION_DAILY
        ]
    ];

    /**
     * @var Server|null
     */
    public ?Server $server = null;

    /**
     * 运行/启动/停止/重启服务 入口
     * @param string $func
     */
    public function run(string $func = 'start')
    {
        $this->$func();
    }

    /**
     * 开启服务
     */
    protected function start()
    {
        if($this->getPid($this->managerProcessName)){
            echo "服务已经启动了，请勿重复开启\n";
            return;
        }
        if(empty($this->config['server'])){
            echo "server配置未设置\n";
            return;
        }
        $serverConfig = $this->setServerConfig($this->config['server'],$this->defaultConfig);
        $this->config['server'] = $serverConfig;

        //处理日志目录
        $logDir = dirname($serverConfig['set']['log_file']);
        if(!is_dir($logDir)){
            mkdir($logDir,0775);
        }

        $this->server = new Server($serverConfig['host'],$serverConfig['port'],$serverConfig['is_base'] ? SWOOLE_BASE : SWOOLE_PROCESS);
        $this->server->set($serverConfig['set']);

        //将server对象存入容器
        Container::set('server',$this);
        $this->server->on('request', function(Request $request, Response $response){
            Container::set('request',$request);
            Container::set('response',$response);
            //运行API
            $this->runApi($request,$response);
        });

        $this->server->on('workerStart',function (\Swoole\Server $server,int $workerId){
            cli_set_process_title($this->workerProcessName);
        });

        cli_set_process_title($this->managerProcessName);
        if(!$serverConfig['is_base']){
            $this->server->on('start',function (\Swoole\Server $server){
                cli_set_process_title($this->masterProcessName);
            });
        }
        $this->server->start();
    }

    /**
     * 停止服务
     */
    protected function stop()
    {
        $pid = $this->getPid($this->managerProcessName);
        if(!empty($pid)){
            exec("kill -15 {$pid}");
        }
        echo "管理进程服务已停止----{$pid}\n";

        $pid = $this->getPid($this->masterProcessName);
        if(!empty($pid)){
            exec("kill -15 {$pid}");
            echo "master进程服务已停止----{$pid}\n";
        }
    }

    /**
     * 重启服务
     */
    protected function reload()
    {
        $pid = $this->getPid($this->managerProcessName);
        if(!empty($pid)){
            exec("kill -USR1 {$pid}");
        }
        echo "服务已重启----{$pid}\n";
    }

    /**
     * 获取管理进程PID
     * @param string $name
     * @return string
     */
    protected function getPid(string $name)
    {
        return exec("pidof {$name}");
    }

    /**
     * 处理server配置
     * @param array $serverConfig
     * @param array $defaultConfig
     * @return array
     */
    protected function setServerConfig(array $serverConfig,array $defaultConfig)
    {
        $response = [];
        foreach ($defaultConfig as $k => $v){
            if(!isset($serverConfig[$k])){
                $response[$k] = $v;
                continue;
            }
            if(is_array($v)){
                $response[$k] = $this->setServerConfig($serverConfig[$k],$v);
                continue;
            }
            $response[$k] = $serverConfig[$k];
        }
        return $response;
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return void
     */
    protected function runApi(Request &$request, Response &$response)
    {
        try{
            //获取controller对象
            [$controller,$action] = $this->getController($request->server['request_uri']);
            if(!method_exists($controller,$action)){
                throw new Exception("api not find path = {$request->server['request_uri']}");
            }
            //运行active
            $isNextRun = $controller->before();
            if($isNextRun){
                return $controller->$action();
            }
            if($response->isWritable()){
                $response->end();
            }
        }catch (\Throwable $e){
            Logs::write('运行错误--'.$e->getMessage().'--'.$e->getFile().'--'.$e->getLine());
            MyResponse::error(500,$e->getMessage());
        }
    }

    /**
     * @param string $path
     * @return array
     * @throws Exception
     */
    protected function getController($path = '/')
    {
        $controller = $this->defaultController;
        $action = $this->defaultAction;
        if($path != '/'){
            $pathArr = explode('/',substr($path,1,strlen($path)));
            if(count($pathArr) == 1){
                throw new Exception("api not find path = {$path}");
            }
            $controller = $pathArr[0];
            $action = $pathArr[1];
        }
        $actionArr = explode('-',$action);
        $action = 'action';
        foreach ($actionArr as $item){
            $action .= ucfirst($item);
        }
        $controller = ucfirst($controller);
        $class = 'App\\Controllers\\' . $controller . 'Controller';
        if(isset($this->controllers[$class])){
            return [$this->controllers[$class],$action];
        }
        $classPath = __DIR__.'/../../Controllers/' . $controller . 'Controller.php';
        if(!is_file($classPath)){
            throw new Exception("api not find path = {$path}");
        }
        $this->controllers[$class] = new $class();
        return [$this->controllers[$class],$action];
    }
}